#include "common.h"

#if !defined(H_CACHE)
#define H_CACHE

namespace Tablebases
{

/*
|			WDL CACHE Statics
\*---------------------------------------------------------------------*/

/*--------------------------------------------------------------------------*/

extern bool_t			DTM_CACHE_INITIALIZED;

extern int				WDL_FRACTION;
extern int				WDL_FRACTION_MAX;
	
extern size_t			DTM_cache_size;
extern size_t			WDL_cache_size;

extern unsigned int		TB_AVAILABILITY;
extern bool_t 			Uncompressed;
extern uint64_t Bytes_read;

extern void
split_index (size_t entries_per_block, index_t i, index_t *o, index_t *r);


/*-	WDL --------------------------------------------------------------------*/

#define DTM_entries_per_block (16 * 1024)
#define WDL_entries_per_unit 4
#define WDL_entry_mask     3
static size_t		WDL_units_per_block = 0;
static bool_t		WDL_CACHE_INITIALIZED = FALSE;
typedef unsigned char unit_t; /* block unit */
typedef struct wdl_block 	wdl_block_t;

struct wdl_block {
	tbkey_t			key;
	index_t			fileoffset;
	index_t 		offset;
	unit_t			*p_arr;
	wdl_block_t		*prev;
	wdl_block_t		*next;
};

struct WDL_CACHE {
	/* defined at init */
	bool_t			cached;
	size_t			max_blocks;
	size_t 			entries_per_block;
	unit_t		 *	buffer;

	/* flushables */
	wdl_block_t	*	top;
	wdl_block_t *	bot;
	size_t			n;
	wdl_block_t *	blocks; /* was entry */

	/* counters */
	uint64_t		hard;
	uint64_t		soft;
	uint64_t		hardmisses;
	uint64_t		hits;
	uint64_t		softmisses;
	uint64_t 		comparisons;
};

extern struct WDL_CACHE 	wdl_cache;

/*- DTM --------------------------------------------------------------------*/

struct dtm_block;

typedef struct dtm_block dtm_block_t;

struct dtm_block {
	tbkey_t			key;
	index_t			fileoffset;
	index_t 		offset;
	dtm_t			*p_arr;
	dtm_block_t		*prev;
	dtm_block_t		*next;
};

struct cache_table {
	/* defined at init */
	bool_t			cached;
	size_t			max_blocks;
	size_t 			entries_per_block;
	dtm_t *			buffer;

	/* flushables */
	dtm_block_t	*	top;
	dtm_block_t *	bot;
	size_t			n;
	dtm_block_t *	entry;

	/* counters */
	uint64_t		hard;
	uint64_t		soft;
	uint64_t		hardmisses;
	uint64_t		hits;
	uint64_t		softmisses;
	unsigned long	comparisons;
};

extern struct cache_table 	dtm_cache;


/*--------------------------------------------------------------------------*/


/*- WDL --------------------------------------------------------------------*/


#if 0
static bool_t			wdl_cache_init (size_t cache_mem);
static void				wdl_cache_flush (void);
static bool_t			get_WDL (tbkey_t key, unsigned side, index_t idx, unsigned int *info_out, bool_t probe_hard_flag);
#endif

extern bool_t			wdl_cache_is_on (void);
static void				wdl_cache_reset_counters (void);
static void				wdl_cache_done (void);

extern wdl_block_t*		wdl_point_block_to_replace (void);
extern bool_t			get_WDL_from_cache (tbkey_t key, index_t fileoffset, index_t idx, unsigned int *out);
static void				wdl_movetotop (wdl_block_t *t);

static unsigned int		wdl_extract (unit_t *uarr, index_t x);
static void				wdl_movetotop (wdl_block_t *t);
/*--------------------------------------------------------------------------*/



/*- DTM --------------------------------------------------------------------*/

extern bool_t			dtm_cache_is_on (void);
static void				dtm_cache_reset_counters (void);
static void				dtm_cache_done (void);

static size_t			dtm_cache_init (size_t cache_mem);
static void				dtm_cache_flush (void);

static void				movetotop (dtm_block_t *t);
extern dtm_block_t *	point_block_to_replace (void);
extern bool_t			get_dtm_from_cache (tbkey_t key, index_t fileoffset, index_t idx, dtm_t *out);
extern dtm_block_t*		dtm_cache_pointblock (tbkey_t key, index_t fileoffset, index_t idx);

/*--------------------------------------------------------------------------*/

/*- Statistics -------------------------------------------------------------------*/

struct TB_STATS {
	long unsigned int wdl_easy_hits  [2]; /* hits that were found in own wdl cache */
	long unsigned int wdl_hard_prob  [2]; /* hard probes to the wdl cache: if fail, they will go to HD */
	long unsigned int wdl_soft_prob  [2]; /* soft probes to the wdl cache: if fail, they won't go to HD */	
	size_t			  wdl_cachesize     ; /* size allocated for wdl cache */
	double			  wdl_occupancy     ; /* % of slots filled in wdl cache */

	long unsigned int dtm_easy_hits  [2]; /* hits that were found in own dtm cache */
	long unsigned int dtm_hard_prob  [2]; /* hard probes to the dtm cache: if fail, they will go to HD */
	long unsigned int dtm_soft_prob  [2]; /* soft probes to the dtm cache: if fail, they won't go to HD */	
	size_t			  dtm_cachesize     ; /* size allocated for dtm cache */
	double			  dtm_occupancy     ; /* % of slots filled in dtm cache */

	long unsigned int  total_hits    [2]; /* succesful probes */
	long unsigned int memory_hits    [2]; /* succesful probes to memory */
	long unsigned int  drive_hits    [2]; /* succesful probes to the Hard drive */
	long unsigned int  drive_miss    [2]; /* failing   probes to the Hard drive */
	long unsigned int  bytes_read    [2]; /* bytes read from Hard drive */
	long unsigned int files_opened      ; /* number of files newly opened */
	double			  memory_efficiency ; /* % hits from memory over total hits */
};

/*--------------------------------------------------------------------------*/



/*- Cache Wrapper -------------------------------------------------------------------*/

struct general_counters {
	/* counters */
	uint64_t		hits;
	uint64_t		miss;
};

extern struct general_counters Drive;

extern bool_t tbcache_is_on (void);
extern void tbstats_get (struct TB_STATS *x);
extern bool_t tbcache_init (size_t cache_mem, int wdl_fraction);
extern bool_t tbcache_restart (size_t cache_mem, int wdl_fraction);
extern void tbcache_done (void);
extern void tbcache_flush (void);
extern void	tbstats_reset (void);

/*--------------------------------------------------------------------------*/


}

#endif
